/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Adam Dunkels <adam@sics.se>
 *
 */

/*
 * This file is a skeleton for developing Ethernet network interface
 * drivers for lwIP. Add code to the low_level functions and do a
 * search-and-replace for the word "ethernetif" to replace it with
 * something that better describes your network interface.
 */

/*-------------------------------------------------------------------------------------------------*/
/* File Name:                                                                                      */
/*     ethernetif.c                                                                                */
/* Description:                                                                                    */
/*     This is the EMC driver for Nuvoton NUC900 series MCU                                       */
/*                                                                                                 */
/* History:                                                                                        */
/*                                                                                                 */
/* Project:                                                                                        */
/*-------------------------------------------------------------------------------------------------*/
 

#include "lwip/opt.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include "NUC900_reg.h"
#include "netif/nuc900_emc.h"

#include "netif/etharp.h"

/* Define those to better describe your network interface. */
#define IFNAME0 'e'
#define IFNAME1 'n'

#define PHY_SETUP_TIME        100 // ticks. Some PHY take extra time to get stablizied after auto-negotiation complete

#define PAGE_SIZE             (4 * 1024)

// move these out from priv 'cos we need them to be aligned
__align(4 * 1024) volatile      RXBD   rx_desc[RX_DESC_SIZE];
__align(4 * 1024) volatile      TXBD   tx_desc[TX_DESC_SIZE];
__align(4 * 1024) volatile char rx_buf[RX_DESC_SIZE][PACKET_BUFFER_SIZE];
__align(4 * 1024) volatile char tx_buf[TX_DESC_SIZE][PACKET_BUFFER_SIZE];



struct ethernetif
{
        struct eth_addr *ethaddr;

        /* Add whatever per-interface state that is needed here. */
};

//static const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};

/* Forward declarations. */
static void  ethernetif_input(struct netif *netif);
static err_t ethernetif_output(struct netif *netif, struct pbuf *p,
                               struct ip_addr *ipaddr);


//#define MAC_DEBUG
//#define MAC_TRACE
#ifdef MAC_DEBUG
#define DEBUG        sysprintf
#else
#define DEBUG(...)
#endif

#ifdef MAC_TRACE
#define TRACE        sysprintf
#else
#define TRACE(...)
#endif

/* Global variables  used for MAC driver */
#define  gMCMDR  (MCMDR_SPCRC | MCMDR_EnMDC | MCMDR_ACP/*|MCMDR_LBK*/)

#define  gMIEN   (EnTXINTR | EnRXINTR | EnRXGD | EnTXCP |EnTxBErr | EnRxBErr | EnTXABT/*| EnTXEMP|EnDEN*/)




struct emcPriv priv;
struct netif *_netif;
static int  _MiiStationWrite(unsigned int PhyInAddr,unsigned int PhyAddr,unsigned int PhyWrData);
static unsigned int _MiiStationRead(unsigned int PhyInAddr, unsigned int PhyAddr);


static void _WriteCam(unsigned char *pval)
{

        unsigned int msw,lsw;

        msw =   (pval[0] << 24) |
                (pval[1] << 16) |
                (pval[2] << 8) |
                pval[3];

        lsw = (pval[4] << 24) |
              (pval[5] << 16);

        outpw(REG_CAMxL_Reg(0),lsw);
        outpw(REG_CAMxM_Reg(0),msw);

}


static int  _ResetPhyChip(void)
{
        unsigned int value;
        unsigned int volatile loop=1000*100;


        _MiiStationWrite(PHY_CNTL_REG, PHYAD, RESET_PHY);
  
        while (1) {
 	            value = _MiiStationRead(PHY_CNTL_REG, PHYAD) ;
                if ((value & RESET_PHY)==0)
                        break;
        }    
                    
        _MiiStationWrite(PHY_ANA_REG, PHYAD, DR100_TX_FULL|DR100_TX_HALF|DR10_TX_FULL|DR10_TX_HALF|IEEE_802_3_CSMA_CD);

        TRACE("\nWait for auto-negotiation complete...");

        value = _MiiStationRead(PHY_CNTL_REG, PHYAD) ;
        if (_MiiStationWrite(PHY_CNTL_REG, PHYAD, value | ENABLE_AN | RESTART_AN)==1) {

                return 1;
        }


        while (1)       /* wait for auto-negotiation complete */
        {

                value = _MiiStationRead(PHY_STATUS_REG, PHYAD) ;

                if (value==(unsigned long)1)
                {
                        DEBUG("ResetPhyChip failed 1\n");
                        return 1;
                }

                if ((value & AN_COMPLETE) != 0 && (value & 4)) // Auto-nego. complete and link valid
                {
                        break;
                }
                loop--;
                if (loop==0)
                {
                        TRACE("FAILED, set to 10 HALF\n");
                        outpw(REG_MCMDR,inpw(REG_MCMDR)&~MCMDR_OPMOD);
                        outpw(REG_MCMDR,inpw(REG_MCMDR)&~MCMDR_FDUP);
                        return 1;
                }
        }

        TRACE("OK\n");

        /* read the result of auto-negotiation */
        value = _MiiStationRead(PHY_ANLPA_REG, PHYAD) ;

       
        if ((value & 0x100)!=0) /* 100MB */
        {
	        TRACE("100MB - FULL\n");
                outpw(REG_MCMDR,inpw(REG_MCMDR)|MCMDR_OPMOD|MCMDR_FDUP);
        }
        else if ((value & 0x80)!=0)
        {
                outpw(REG_MCMDR,inpw(REG_MCMDR)&~MCMDR_FDUP);
                outpw(REG_MCMDR,inpw(REG_MCMDR)|MCMDR_OPMOD);
                TRACE("100MB - HALF\n");
        }
        else if ((value & 0x40)!=0)/* Full Duplex */
        {
                TRACE("10MB - FULL\n");
                outpw(REG_MCMDR,inpw(REG_MCMDR)&~MCMDR_OPMOD);
                outpw(REG_MCMDR,inpw(REG_MCMDR)|MCMDR_FDUP);
        }
        else 
        { 
                TRACE("10MB - HALF\n");
                outpw(REG_MCMDR,inpw(REG_MCMDR)&~MCMDR_OPMOD);
                outpw(REG_MCMDR,inpw(REG_MCMDR)&~MCMDR_FDUP);
        }       
       
        // cannot use sysDelay here. this function might be called in timer ISR
        loop = 0xf0000;
        while(loop-- > 0);
        
        return 0;
}

static void _InitDescriptors(void)
{
        int i;

        priv.start_tx_ptr = sysGetPhyPageAddr((unsigned int)&tx_desc[0]);
        priv.start_tx_buf = sysGetPhyPageAddr((unsigned int)&tx_buf[0]);

        priv.start_rx_ptr = sysGetPhyPageAddr((unsigned int)&rx_desc[0]);
        priv.start_rx_buf = sysGetPhyPageAddr((unsigned int)&rx_buf[0]);		


        DEBUG(" tx  start_tx_ptr %x\n",priv.start_tx_ptr);

        for ( i = 0 ; i < TX_DESC_SIZE ; i++ ) {

	//sysprintf("%08x  %08x\n", sysGetPhyPageAddr((unsigned int)&tx_buf[i]), &tx_buf[i] );
	
                tx_desc[i].SL = 0;
                tx_desc[i].mode = 0;
                tx_desc[i].buffer = sysGetPhyPageAddr((unsigned int)&tx_buf[i]);
                tx_desc[i].next = sysGetPhyPageAddr((unsigned int)&tx_desc[i+1]);
                
                DEBUG(" *tx cur %d desc %x buffer  %x", i,  &tx_desc[i],tx_desc[i].buffer);
                DEBUG("  next %x\n",tx_desc[i].next);
        }
        tx_desc[i-1].next = sysGetPhyPageAddr((unsigned int)&tx_desc[0]);

        
        
        DEBUG(" * cur %d desc %x buffer  %x", i-1,  &tx_desc[i-1],tx_desc[i-1].buffer);
        DEBUG("  next %x\n",tx_desc[i-1].next);


        DEBUG(" rx start_rx_ptr %x\n",priv.start_rx_ptr);

        for ( i =0 ; i < RX_DESC_SIZE ; i++) {
       
                rx_desc[i].SL = RXfOwnership_DMA;

                rx_desc[i].buffer = sysGetPhyPageAddr((unsigned long)&rx_buf[i]);
                rx_desc[i].next = sysGetPhyPageAddr((unsigned long)&rx_desc[i+1]);

                DEBUG(" #,desc %d desc-addr  %x", i, &rx_desc[i]);
                DEBUG("  next %x\n",rx_desc[i].next);
        }
        rx_desc[i-1].next = sysGetPhyPageAddr((unsigned long)&rx_desc[0]);
}


static void _ResetMAC(void)
{

        unsigned int val=inpw(REG_MCMDR);

        outpw(REG_MCMDR,inpw(REG_MCMDR)&~(MCMDR_TXON|MCMDR_RXON));
        outpw(REG_MCMDR,inpw(REG_MCMDR)|SWR); 


        _InitDescriptors();

        memset(&priv.stats, 0, sizeof(struct net_device_stats));
        priv.cur_tx_entry=0;
        priv.cur_rx_entry=0;
        priv.rx_ptr=priv.start_rx_ptr ;
        priv.tx_ptr=priv.start_tx_ptr ;


        outpw(REG_RXDLSA,priv.start_rx_ptr);
        outpw(REG_TXDLSA,priv.start_tx_ptr);
        outpw(REG_DMARFC,PACKET_SIZE);

        _WriteCam((unsigned char *)(MAC_ADDR));

        outpw(REG_CAMEN,inpw(REG_CAMEN) | 1);  // enable entry 0

        outpw(REG_CAMCMR,CAMCMR_ECMP|CAMCMR_ABP/*|CAMCMR_AMP*/);  // No multicast packets.

        /* Configure the MAC control registers. */
        outpw(REG_MIEN,gMIEN);

        outpw(REG_MCMDR,MCMDR_TXON|MCMDR_RXON|val|gMCMDR);
        outpw(REG_TSDR, 0);
        outpw(REG_RSDR, 0);

        outpw(REG_MISTA,inpw(REG_MISTA)); //clear interrupt
}

void _Autodetect(void)
{

        unsigned int value;

        value = _MiiStationRead(PHY_STATUS_REG, PHYAD) ;


        if ((value&0x20)==0) {
                if (!priv.plugout) {
                        TRACE("MAC Line-off...\n");
                        priv.plugout=1;
                        sysDisableInterrupt(INT_EMCTXINT);
                        sysDisableInterrupt(INT_EMCRXINT);
                       
                }

        } else {
                if (priv.plugout) {
                        TRACE("MAC Line-on...\n");
                        _ResetMAC(); 
                        _ResetPhyChip();
                        sysEnableInterrupt(INT_EMCTXINT);
                        sysEnableInterrupt(INT_EMCRXINT);
                        priv.plugout=0;
                        
                }
        }

}

static int _MiiStationWrite(unsigned int phyInAddr,unsigned int phyAddr,unsigned int phyWrData)
{
        volatile int i = 1000;
        volatile int loop=1000*100;

        outpw(REG_MIID,phyWrData);
        outpw(REG_MIIDA,phyInAddr|phyAddr|PHYBUSY|PHYWR|MDCCR);
        while (i--);
        while ((inpw(REG_MIIDA) &PHYBUSY)) {
                loop--;
                if (loop==0)
                        return 1;
        }
        return 0;
}


static unsigned int _MiiStationRead(unsigned int phyInAddr, unsigned int phyAddr)
{
        unsigned int phyRdData ;
        volatile int loop=1000*100;

        outpw(REG_MIIDA, phyInAddr | phyAddr | PHYBUSY | MDCCR);
        while ( (inpw(REG_MIIDA)& PHYBUSY) ) {
                loop--;
                if (loop==0)
                    return(1);
        }

        phyRdData = inpw(REG_MIID);
        return phyRdData ;
}



static void _TxInterrupt(void)
{

        unsigned int status;
        unsigned int cur_ptr;
        volatile TXBD  *txbd;

        status=inpw(REG_MISTA);   //get  interrupt status;
        outpw(REG_MISTA,status&0xFFFF0000);  //clear interrupt
        cur_ptr=inpw(REG_CTXDSA);
        while (( sysGetPhyPageAddr((unsigned int)&(tx_desc[priv.cur_tx_entry])) != (unsigned int)cur_ptr)) {
        
                txbd = &tx_desc[priv.cur_tx_entry];
                priv.cur_tx_entry = (priv.cur_tx_entry+1) & (TX_DESC_SIZE - 1);

                TRACE("*txbd->SL %x\n",txbd->SL);
                TRACE("priv->tx_ptr %x  cru_ptr =%x\n",priv.tx_ptr,cur_ptr);
                if (txbd->SL &TXDS_TXCP) {
                        priv.stats.tx_packets++;
                } else {
                        priv.stats.tx_errors++;
                }

                txbd->SL=0;
                txbd->mode=0;

        }
#ifdef MAC_DEBUG
        if (status & MISTA_EXDEF)
                DEBUG("MISTA_EXDEF\n");


        if (status&MISTA_TxBErr)
                DEBUG("MISTA_TxBErr\n");
#endif

}


static void _RxInterrupt(void)
{

        unsigned long status;


        status = inpw(REG_MISTA);   //get interrupt status;

        outpw(REG_MISTA, status & 0xFFFF); //clear interrupt
        priv.cur_rx_entry++;
        if (status & (MISTA_RDU|MISTA_RxBErr)) {

                priv.is_rx_all=RX_DESC_SIZE; //receive all
                ethernetif_input(_netif);    //start doing
                priv.is_rx_all=0;
                if (status&MISTA_RxBErr) {
                        DEBUG("MISTA_RxBErr\n");
                        _ResetMAC();
                }
                outpw(REG_RSDR ,0);
                TRACE("rx_interrupt MISTA %x \n",status);

                return ;
        }

        ethernetif_input(_netif);
}


static void _OpenMAC(struct netif *netif)
{


//        sysInstallISR(IRQ_LEVEL_1, INT_EMCTXINT, (PVOID)_TxInterrupt, (void *)netif);
//        sysInstallISR(IRQ_LEVEL_1, INT_EMCRXINT, (PVOID)_RxInterrupt, (void *)netif);

	sysInstallISR(IRQ_LEVEL_1, INT_EMCTXINT, (PVOID)_TxInterrupt);
	sysInstallISR(IRQ_LEVEL_1, INT_EMCRXINT, (PVOID)_RxInterrupt);
        sysEnableInterrupt(INT_EMCTXINT);
        sysEnableInterrupt(INT_EMCRXINT);
        
        TRACE("MAC interrupt enabled\n");
        sysSetLocalInterrupt(ENABLE_IRQ);
         

}

static void _EmcInit(struct netif *netif)
{
    unsigned int i;

    i = inpw(REG_MFSEL);
    i |= 0x2;
    outpw(REG_MFSEL, i);

    i = inpw(REG_CLKEN);
    i |= 0x80;
    outpw(REG_CLKEN, i);
    
    i = inpw(REG_CLKEN1);
    i |= 0x4;
     outpw(REG_CLKEN1, i);

        _ResetMAC();
        if (_ResetPhyChip() == 1)
                priv.plugout=1;

        TRACE("Enable auto detect timer\n");
        sysSetTimerEvent(TIMER0, AUTO_DETECT_INTERVAL, (PVOID)_Autodetect);
        TRACE("open MAC\n");
        _OpenMAC(netif);

}


static void low_level_init(struct netif *netif)
{
        //struct ethernetif *ethernetif = netif->state;

        /* set MAC hardware address length */
        netif->hwaddr_len = MAC_LEN;

        /* set MAC hardware address */
        memcpy(&netif->hwaddr[0], (void *)(MAC_ADDR), MAC_LEN);


        /* maximum transfer unit */
        netif->mtu = 1500;

        /* broadcast capability */
        netif->flags = NETIF_FLAG_BROADCAST;

        /* Do whatever else is needed to initialize interface. */

		_netif = netif;
        _EmcInit(netif);

}

/*
 * low_level_output():
 *
 * Should do the actual transmission of the packet. The packet is
 * contained in the pbuf that is passed to the function. This pbuf
 * might be chained.
 *
 */

static err_t low_level_output(struct netif *netif, struct pbuf *p)
{
        //struct ethernetif *ethernetif = netif->state;
        struct pbuf *q;
        unsigned int length = 0, tmp;
        TXBD *txbd =( TXBD *)priv.tx_ptr;


#if ETH_PAD_SIZE
        pbuf_header(p, -ETH_PAD_SIZE);                  /* drop the padding word */
#endif

        for (q = p; q != NULL; q = q->next)
        {
                /* Send the data from the pbuf to the interface, one pbuf at a
                   time. The size of the data in each pbuf is kept in the ->len
                   variable. */

                if (length + q->len > 1514)
                        break; // the packet will lost some data anyway, ignore the rest of chain
                memcpy((char *)txbd->buffer + length, q->payload, q->len);
                length += q->len;

        }
#if 0        
        {        
                int i;
                for(i = 0; i < length; i++) {
                      if((i & 0xf) == 0)
                              sysprintf("\n");
                      sysprintf(" %02x", *(char *)((char *)(txbd->buffer) + i));      
                }        
        
        }
#endif        
        txbd->SL=length&0xFFFF;
        txbd->mode=(PaddingMode | CRCMode | MACTxIntEn);
        txbd->mode|= TXfOwnership_DMA;
        tmp = inpw(REG_MCMDR);
        if (!(tmp & MCMDR_TXON))
                outpw(REG_MCMDR,tmp|MCMDR_TXON); // can really get here?
        outpw(REG_TSDR ,0);

        txbd=(TXBD *)txbd->next;
        priv.tx_ptr=(unsigned long)txbd;


#if ETH_PAD_SIZE
        pbuf_header(p, ETH_PAD_SIZE);                   /* reclaim the padding word */
#endif

#if LINK_STATS
        lwip_stats.link.xmit++;
#endif /* LINK_STATS */

        return ERR_OK;
}


/*
 * ethernetif_output():
 *
 * This function is called by the TCP/IP stack when an IP packet
 * should be sent. It calls the function called low_level_output() to
 * do the actual transmission of the packet.
 *
 */

static err_t ethernetif_output(struct netif *netif, struct pbuf *p,
                  struct ip_addr *ipaddr)
{

        /* resolve hardware address, then send (or queue) packet */
        return etharp_output(netif, ipaddr, p);

}

/*
 * ethernetif_input():
 *
 * This function should be called when a packet is ready to be read
 * from the interface. It uses the function low_level_input() that
 * should handle the actual reception of bytes from the network
 * interface.
 *
 */

static void ethernetif_input(struct netif *netif)
{
        struct ethernetif *ethernetif = netif->state;
        RXBD *rxbd = (RXBD *)priv.rx_ptr ;
        struct eth_hdr *ethhdr;
        struct pbuf *p;
        unsigned int length;
        unsigned int status;
        int _flag=0;
        u16_t len;


        do
        {

                if (priv.is_rx_all>0) {
                        _flag=1;
                        --priv.is_rx_all;
                } else if (_flag==1) {
                        _flag=0;
                        break;
                } else if ((inpw(REG_CRXDSA)== sysGetPhyPageAddr((unsigned int)rxbd))) {
                        break;
                }

                length = rxbd->SL & 0xFFFF;
                status = (rxbd->SL & 0xFFFF0000)&((unsigned long)~0 >>2);

                if (status & RXDS_RXGD) {
                        unsigned char *data = (unsigned char *) rxbd->buffer;
                        len = length;

#if ETH_PAD_SIZE
                        len += ETH_PAD_SIZE;    /* allow room for Ethernet padding */
#endif

                        /* We allocate a pbuf chain of pbufs from the pool. */
                        /* For better performance, we use PBUF_RAM instead. */
                        
                      //  p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
                        /* How about allocate pbuf from RAM for better performance? */ 
                        // FIXME: This could eat up all memory if we set up middle layer & traffic is too heavy....
                        p = pbuf_alloc(PBUF_RAW, len, PBUF_RAM);
                        if (p != NULL) {

#if ETH_PAD_SIZE
                                pbuf_header(p, -ETH_PAD_SIZE);                  /* drop the padding word */
#endif

                               // for (q = p; q != NULL; q = q->next) {
                                        /* Read enough bytes to fill this pbuf in the chain. The
                                                * available data in the pbuf is given by the q->len
                                                * variable. */
                                        //read data into(q->payload, q->len);
                                        memcpy(/*q->payload*/ p->payload, data, /*q->len*/ length);
                              //          if (q->len >= length )
                                //                break;
                                  //      length -= q->len;
                                    //    data += q->len;
                               // }
#if ETH_PAD_SIZE
                                pbuf_header(p, ETH_PAD_SIZE);                   /* reclaim the padding word */
#endif

#if LINK_STATS
                                lwip_stats.link.recv++;
#endif

                        } else {
#if LINK_STATS
                                lwip_stats.link.memerr++;
                                lwip_stats.link.drop++;
#endif /* LINK_STATS */
                                DEBUG("No memory for Rx\n");
                                priv.stats.rx_dropped++;
                                goto freeDes;

                        }
                        priv.stats.rx_packets++;
                        priv.stats.rx_bytes += length;

                } else {

                      //  if (priv.is_rx_all==RX_DESC_SIZE)
                        //        DEBUG("Rx error:%x,rxbd:%x,priv->is_rx_all:%d\n",status,rxbd,priv.is_rx_all);
                        priv.stats.rx_errors++;
#ifdef MAC_DEBUG                        
                        if (status & RXDS_RP ) {
                                DEBUG("MAC: Receive Runt Packet Drop it!\n");
                              //  priv.stats.rx_length_errors++;
                        }
                        if (status & RXDS_CRCE ) {
                                DEBUG("MAC Receive CRC  Packet Drop It! \n");
                              //  priv.stats.rx_crc_errors ++;
                        }
                        if (status & RXDS_ALIE ) {
                                DEBUG("MAC Receive Aligment Packet Dropt It!\n");
                              //  priv.stats.rx_frame_errors++;
                        }

                        if (status &  RXDS_PTLE) {
                                DEBUG("MAC Receive Too Long  Packet Dropt It!\n");
                              //  priv.stats.rx_over_errors++;
                        }
#endif
                        goto freeDes;

                }


                /* points to packet payload, which starts with an Ethernet header */
                ethhdr = p->payload;

#if LINK_STATS
                lwip_stats.link.recv++;
#endif /* LINK_STATS */

               // ethhdr = p->payload;

                switch (htons(ethhdr->type)) {
                        /* IP packet? */
                case ETHTYPE_IP:
                        /* update ARP table */
                        etharp_ip_input(netif, p);
                        /* skip Ethernet header */
                        pbuf_header(p, -sizeof(struct eth_hdr));
                        /* pass to network layer */
                        netif->input(p, netif);
                        break;

                case ETHTYPE_ARP:
                        /* pass p to ARP module  */
                        etharp_arp_input(netif, ethernetif->ethaddr, p);
                        break;
                default:
                        pbuf_free(p);
                        p = NULL;
                        break;
                }
freeDes:
                rxbd->SL =RXfOwnership_DMA;
                rxbd->reserved = 0;
                priv.rx_ptr=(unsigned long)rxbd->next;
                rxbd=(RXBD *)priv.rx_ptr;

        } while (1);
        priv.is_rx_all = 0;
}

void arp_timer(void)
{
        etharp_tmr();
}

/*
 * ethernetif_init():
 *
 * Should be called at the beginning of the program to set up the
 * network interface. It calls the function low_level_init() to do the
 * actual setup of the hardware.
 *
 */

err_t ethernetif_init(struct netif *netif)
{
        struct ethernetif *ethernetif;


        ethernetif = mem_malloc(sizeof(struct ethernetif));

        if (ethernetif == NULL)
        {
                LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
                return ERR_MEM;
        }
        

        sysSetCachePages((unsigned int)&rx_desc[0], ((sizeof(rx_desc) / PAGE_SIZE) + 1) * PAGE_SIZE, CACHE_DISABLE);
        sysSetCachePages((unsigned int)&tx_desc[0], ((sizeof(tx_desc) / PAGE_SIZE) + 1) * PAGE_SIZE, CACHE_DISABLE);        
        sysSetCachePages((unsigned int)&rx_buf[0][0], ((sizeof(rx_buf) / PAGE_SIZE) + 1) * PAGE_SIZE, CACHE_DISABLE);
        sysSetCachePages((unsigned int)&tx_buf[0][0], ((sizeof(tx_buf) / PAGE_SIZE) + 1) * PAGE_SIZE, CACHE_DISABLE);
                

        netif->state = ethernetif;
        netif->name[0] = IFNAME0;
        netif->name[1] = IFNAME1;
        netif->output = ethernetif_output;
        netif->linkoutput = low_level_output;

        ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);

        low_level_init(netif);

        etharp_init();

        sysSetTimerEvent(TIMER0, ARP_TMR_INTERVAL/10, (PVOID)arp_timer);        
        
        return ERR_OK;
}

